feat(guardrails): send attached file references with llm-as-judge evaluation - #1082
Merged
Merged
Conversation
apetraru-uipath
force-pushed
the
feat/guardrail-judge-file-support
branch
2 times, most recently
from
September 17, 2026 07:44
5137471 to
908f604
Compare
apetraru-uipath
marked this pull request as ready for review
September 17, 2026 07:44
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved attachment-reference safety and shared metadata concurrency issues need to be addressed.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
Adds feature-flagged attachment references to low-code LLM-as-Judge guardrail evaluations.
Changes:
- Projects supported attachments into guarded backend references.
- Adds asynchronous evaluation and attachment-related 400 fallback.
- Updates dependencies and expands regression coverage.
File summaries
| File | Description |
|---|---|
uv.lock |
Relocks updated dependencies. |
tests/cli/test_agent_with_guardrails.py |
Updates test guardrail mocks. |
tests/cli/conftest.py |
Updates guardrail mock signatures. |
tests/agent/guardrails/test_guardrail_nodes.py |
Tests forwarding, fallback, and async evaluation. |
tests/agent/guardrails/test_attachment_refs.py |
Tests reference filtering and scoping. |
src/uipath_langchain/agent/guardrails/guardrail_nodes.py |
Sends references and handles evaluation fallback. |
src/uipath_langchain/agent/guardrails/attachment_refs.py |
Builds feature- and scope-gated attachment references. |
pyproject.toml |
Updates version and platform dependency. |
Review details
Suppressed comments (1)
src/uipath_langchain/agent/guardrails/guardrail_nodes.py:248
metadatais captured by the node closure and installed as the graph node's metadata, but this path now awaitsasyncio.to_thread. Concurrent invocations of the same compiled graph can therefore interleave and overwritemetadata["payload"], causing one run's observability payload to be reported for another run. Make the payload metadata invocation-scoped (or otherwise protect/copy it) rather than mutating this shared dictionary.
# Generate and store payload for observability. Generated once and passed
# down: it used to run again inside _evaluate_builtin_guardrail.
payload = payload_generator(state)
if execution_stage == ExecutionStage.PRE_EXECUTION:
metadata["payload"]["input"] = payload
else:
metadata["payload"]["output"] = payload
- Files reviewed: 7/8 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
apetraru-uipath
force-pushed
the
feat/guardrail-judge-file-support
branch
from
September 17, 2026 08:18
f87bc01 to
3da8afc
Compare
valentinabojan
approved these changes
Sep 17, 2026
apetraru-uipath
force-pushed
the
feat/guardrail-judge-file-support
branch
2 times, most recently
from
September 17, 2026 13:50
842d463 to
e48ced5
Compare
…luation Agent-scope and LLM-scope built-in guardrails on low-code agents now forward the run's job attachments to the guardrails backend, so a guardrail can evaluate what is in a file rather than the serialized metadata that currently lands in the prompt. - New agent/guardrails/attachment_refs.py projects state.inner_state.job_attachments into references (id, file name, mime type). The backend resolves the id through Orchestrator; the runtime makes no network call and never resolves a URL. No feature flag, validator or file-type filter on the runtime side: the backend's EnableGuardrailLlmAsJudgeAttachments flag decides whether the references are used at all, and the backend decides which validators and types it can inspect (today: llm_as_judge, text/pdf/images). The guardrail's appliesTo parameter (Prompts) is the only runtime-side opt-out. At most five references. Never raises: entries are built one by one and a malformed one is skipped without consuming a slot. Backward compatible: a backend without attachment support ignores the extra property and header. - guardrail_nodes.py: payload generated once per evaluation, evaluate_guardrail offloaded to a thread, attachments resolved at Agent and LLM scope only, and a 400 on a request carrying attachments is retried without them so a file can never fail the run. - Requires uipath-platform>=0.2.31 (GuardrailAttachment); version 0.18.9. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
apetraru-uipath
force-pushed
the
feat/guardrail-judge-file-support
branch
from
September 17, 2026 14:24
e48ced5 to
3779e92
Compare
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



What
Agent-scope and LLM-scope LLM-as-Judge guardrails on low-code agents now forward the run's job attachments to the guardrails backend as references (
id,fileName,mimeType), so the judge can evaluate what is in the file rather than its metadata.agent/guardrails/attachment_refs.pyprojectsstate.inner_state.job_attachmentsinto attachment references — no runtime feature flag: the runtime forwards references for every built-in guardrail and every file type whenever the run has attachments (the guardrail'sappliesTo = Promptsis the only runtime-side opt-out), and the backend'sEnableGuardrailLlmAsJudgeAttachmentsflag decides whether they are used at all and which validators and types it can inspect (todayllm_as_judge, text/pdf/images). Backward compatible: a backend without attachment support ignores the extra property and header (UnmappedMemberHandling = Skip). It never raises: the low-code guardrail node re-raises everything, so a malformed entry must not kill the run.guardrail_nodes.py: the payload generator runs once per evaluation,evaluate_guardrailis offloaded withasyncio.to_thread, and a backend 400 on a request that carried attachments is retried text-only rather than terminating the run.Out of scope by design: the coded-agent middleware and decorator flavors, and tool scope.
v2 — no SAS resolution in the runtime (commit
51374716)The first version resolved each attachment to a signed URL via
get_blob_file_access_uri_asyncand sent the URL. The backend now resolves ids itself through Orchestrator (UiPath/Agents#6256), so the runtime forwards references only: no Orchestrator round-trip per file, no credential on the wire, no URL redaction. 320 guardrail tests pass; ruff and ruff format clean; mypy clean in guardrail files (the remaining errors are pre-existing missing-extras imports inchat/_legacyandagent/advanced/code_interpreter.py).Merge order: UiPath/uipath-python#1895 (merged, published as
uipath-platform0.2.31) first; this PR now depends on that release.Review fixes (
d9d57812)_create_guardrail_nodehad no scope check and the tool node routes through it, so a tool-scope judge would have shipped file contents on every tool call. Now Agent and LLM scope only, per the product decision.metadata["payload"]["attachments"]: nothing consumed it (the Python side creates no evaluation span for built-in guardrails; helix's span already records attachments) and it was never cleared across turns.Scope selector (
06a64b0c)The judge guardrail gained an optional
appliesToparameter (Prompts / Files / Both) inUiPath/Agents#6256. The backend gates on it as well, but only after the SAS urls are resolved, so reading it here is what actually saves an Orchestrator round-trip per file on a prompts-only guardrail.Absent, unrecognized, or unreadable keeps files in scope, matching the backend default of Both — silently stopping file scanning for a guardrail whose author never asked for that is the worse direction. The parameter id is matched case-insensitively, as the backend matches it.
Notes for reviewers
attachment_refs.pynever raises. The low-code guardrail node re-raises everything it sees, which terminates the agent run — so a transient Orchestrator failure must never escape. Gated onappliesToonly; no feature flag, validator or type filter on the runtime side — the backend flag is the single kill switch.Two pre-existing bugs fixed along the way, both with regression tests: the payload generator ran twice per evaluation, and the synchronous
evaluate_guardrailblocked the event loop (nowasyncio.to_thread).Out of scope by design: the coded-agent middleware and decorator flavors.
Test plan
tests/agent/guardrails/— 305 passed (baseline 279).tests/cli/55,tests/guardrails/253 (coded flavors, untouched). ruff, ruff format, mypy (touched files) clean.Depends on: UiPath/uipath-python#1895 · pairs with UiPath/Agents#6256
🤖 Generated with Claude Code